package com.hp.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.List;

/**
 * 用于高精确处理常用的数学运算
 *
 * @author hp
 */
@SuppressWarnings("unused")
public class ArithmeticUtils {
    //默认除法运算精度
    private static final int DEF_DIV_SCALE = 10;

    private static final BigDecimal ZERO = BigDecimal.ZERO;
    private static final BigDecimal ONE = BigDecimal.ONE;

    /**
     * 提供精确的加法运算
     *
     * @param v 加数
     * @return 参数的和 double
     */
    public static double addDoubles(double... v) {
        BigDecimal result = ZERO;
        for (double item : v) {
            result = result.add(new BigDecimal(Double.toString(item)));
        }
        return result.doubleValue();
    }

    public static double addDoubles(Collection<Double> v) {
        BigDecimal result = ZERO;
        for (double item : v) {
            result = result.add(new BigDecimal(Double.toString(item)));
        }
        return result.doubleValue();
    }


    /**
     * 提供精确的加法运算
     *
     * @param v     加数
     * @param scale 保留scale 位小数
     * @return 参数的和
     */
    public static String addStrings(int scale, String... v) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal result = ZERO;
        for (String item : v) {
            result = add(result, new BigDecimal(item));
        }
        return result.setScale(scale, RoundingMode.HALF_UP).toString();
    }


    public static String addStrings(int scale, Collection<String> v) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal result = ZERO;
        for (String item : v) {
            result = add(result, new BigDecimal(item));
        }
        return result.toPlainString();
    }

    /**
     * 提供精确的加法运算
     *
     * @param v     加数
     * @param scale 保留scale 位小数
     * @return 参数的和
     */
    public static BigDecimal addBigDecimals(int scale, Collection<BigDecimal> v) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal result = ZERO;
        for (BigDecimal item : v) {
            result = add(result, item);
        }
        return result;
    }

    /**
     * 提供精确的加法运算
     *
     * @param v     加数
     * @param scale 保留scale 位小数
     * @return 参数的和
     */
    public static BigDecimal addBigDecimals(int scale, BigDecimal... v) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal result = ZERO;
        for (BigDecimal item : v) {
            result = add(result, item);
        }
        return result;
    }
    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 参数的和 BigDecimal
     */
    public static BigDecimal add(BigDecimal v1, BigDecimal v2) {
        if (v1 == null || v2 == null) {
            return ZERO;
        }
        return v1.add(v2);
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1 被减数
     * @param v  减数
     * @return 参数的差 double
     */
    public static double subDoubles(double v1, double... v) {
        BigDecimal result = new BigDecimal(Double.toString(v1));

        for (double item : v) {
            result = result.subtract(new BigDecimal(Double.toString(item)));
        }
        return result.doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v  减数
     * @return 参数的差 BigDecimal
     */
    public static BigDecimal subStrings(String v1, String... v) {
        BigDecimal result = new BigDecimal(v1);
        for (String item : v) {
            result = result.subtract(new BigDecimal(item));
        }
        return result;
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1    被减数
     * @param v     减数
     * @param scale 保留scale 位小数
     * @return 参数的差
     */
    public static String subStrings(int scale, String v1, String... v) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal result = new BigDecimal(v1);
        for (String item : v) {
            result = result.subtract(new BigDecimal(item));
        }
        return result.setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v 乘数
     * @return 参数的积 double
     */
    public static double mul(double... v) {
        BigDecimal result = ONE;
        for (double item : v) {
            result = result.multiply(new BigDecimal(Double.toString(item)));
        }
        return result.doubleValue();
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v 乘数
     * @return 参数的积 BigDecimal
     */
    public static BigDecimal mul(String... v) {
        BigDecimal result = ONE;
        for (String item : v) {
            result = result.multiply(new BigDecimal(item));
        }
        return result;
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v     乘数
     * @param scale 保留scale 位小数
     * @return 参数的积 double
     */
    public static double mul(int scale, double... v) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal result = ONE;
        for (double item : v) {
            result = result.multiply(new BigDecimal(Double.toString(item)));
        }
        return result.setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v     乘数
     * @param scale 保留scale 位小数
     * @return 参数的积
     */
    public static String mul(int scale, String... v) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal result = ONE;
        for (String item : v) {
            result = result.multiply(new BigDecimal(item));
        }
        return result.setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到
     * 小数点以后10位，以后的数字四舍五入
     *
     * @param v1 被除数
     * @param v  除数
     * @return 参数的商
     */

    public static double div(double v1, double... v) {
        return div(DEF_DIV_SCALE, v1, v);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v     除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 参数的商
     */
    public static double div(int scale, double v1, double... v) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal result = new BigDecimal(Double.toString(v1));
        for (double item : v) {
            if (item == 0) {
                throw new ArithmeticException("Division by zero");
            }
            result = result.divide(new BigDecimal(Double.toString(item)), scale, RoundingMode.HALF_UP);
        }
        return result.doubleValue();
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v     除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 参数的商
     */
    public static String div(int scale, String v1, String... v) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal result = new BigDecimal(v1);
        for (String item : v) {
            result = result.divide(new BigDecimal(item), scale, RoundingMode.HALF_UP);
        }
        return result.toString();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static String round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * 取余数
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static String remainder(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.remainder(b2).setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * 取余数  BigDecimal
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.remainder(v2).setScale(scale, RoundingMode.HALF_UP);
    }

    /**
     * 比较大小
     *
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于v2 则 返回true 否则false
     */
    public static boolean compare(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.compareTo(b2) > 0;
    }

    /**
     * 比较大小
     *
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于v2 则 返回true 否则false
     */
    public static boolean compare(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.compareTo(b2) > 0;
    }

    /**
     * 获取百分比
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 百分比
     */
    public static String percent(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, scale, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).toString();
    }



}